// 组件的 provide 和 inject 功能  （父组件通过provide提供的数据，可以让任意子孙组件通过inject获取）  例子在example\api-provide-inject
// 这里获取数据要注意，需要向上找所有祖组件，不能单纯只找父组件 ，同时，如果同时存在多个相同键值，取 离自己组件最近的那个
//（我原本的想法是用循环去查找，经过视频讲解之后知道了更好的方法，就是通过原型链，会自动向上查找） 


//除了普通的提供、获取数据的需求，还有特殊需求
//1. 如果父级全都没有提供这个键值，那么可以通过 inject 的第二个参数，返回这个默认值
//2. 这个默认值还可以是个函数，会执行函数，返回结果

import { key } from "../reactivity/index.d";
import { getCurrentInstance } from "./component";


/**provide-inject 之提供数据。 **必须在setup中才能使用**
 * @param key 键
 * @param value 值
 */
export function provide(key: key, value: any) {
    /**获取当前组件实例 */
    const currentInstance = getCurrentInstance() //因为这俩函数必须在setup中使用，所以我们也可以在这里使用 getCurrentInstance
    if (currentInstance) {
        let { provides, parent } = currentInstance
        /**父组件的provides */
        const parentProvides = parent?.provides

        //如果父组件的和自己的是一样的，说明是初始化  （因为在createComponentInstance函数中，执行了 provides: parent ? parent.provides : {} 这句代码 ）
        //第二次调用provide的时候，因为已经在这个if中赋值了，就不会再经过if了 ,避免覆盖了
        if (parentProvides === provides) {
            //把当前的provides的原型设置为父组件的provides
            provides = currentInstance.provides = Object.create(parentProvides) //使用 Object.create 设置原型，这一步只能执行一次，避免覆盖
        }


        provides[key] = value //存
    }
}

/**provide-inject 之获取数据 **必须在setup中才能使用**
 * @param key  键
 * @param defaultVal  如果没取到provide的数据，就返回这个默认值 （可以是函数）
 */
export function inject(key: key, defaultVal?: any) {
    /**获取当前组件实例 */
    const currentInstance = getCurrentInstance() //因为这俩函数必须在setup中使用，所以我们也可以在这里使用 getCurrentInstance
    if (currentInstance) {
        const { parent } = currentInstance
        const parentProvide = parent?.provides
        if (parentProvide && key in parentProvide) {//如果key在parentProvide中有，就返回
            return parentProvide[key]
        } else if (defaultVal) {//没有的话就返回自身的默认值
            if (typeof defaultVal === 'function') {
                return defaultVal() //是函数的话给出函数返回值
            } else {
                return defaultVal
            }
        } else {
            return undefined //都没命中就给undefined
        }

    }
}
